package com.duobaoyu.dby.supply.db.util;

import com.duobaoyu.dby.common.core.constant.CommonConstants;
import com.duobaoyu.dby.common.core.constant.StringPool;
import com.duobaoyu.dby.common.mybatis.annotation.Ignore;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;

import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.Objects;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName ExecutorPluginUtils
 * @Description sql拦截器工具类
 * @createTime 10:28:00 2022/06/24
 */
public class ExecutorPluginUtils {

	/**
	 * 获取sql语句
	 * @param invocation
	 * @return
	 */
	public static String getSqlByInvocation(Invocation invocation) {
		final Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[CommonConstants.ZERO];
		Object parameterObject = args[CommonConstants.ONE];
		BoundSql boundSql = ms.getBoundSql(parameterObject);
		return boundSql.getSql();
	}

	/**
	 * 包装sql后，重置到invocation中
	 * @param invocation
	 * @param sql
	 * @throws SQLException
	 */
	public static void resetSql2Invocation(Invocation invocation, String sql) throws SQLException {
		final Object[] args = invocation.getArgs();
		MappedStatement statement = (MappedStatement) args[CommonConstants.ZERO];
		Object parameterObject = args[CommonConstants.ONE];
		BoundSql boundSql = statement.getBoundSql(parameterObject);
		MappedStatement newStatement = newMappedStatement(statement, new BoundSqlSqlSource(boundSql));
		MetaObject msObject =  MetaObject.forObject(newStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),new DefaultReflectorFactory());
		msObject.setValue("sqlSource.boundSql.sql", sql);
		args[CommonConstants.ZERO] = newStatement;
	}


	private static MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
		MappedStatement.Builder builder =
				new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
		builder.resource(ms.getResource());
		builder.fetchSize(ms.getFetchSize());
		builder.statementType(ms.getStatementType());
		builder.keyGenerator(ms.getKeyGenerator());
		if (ms.getKeyProperties() != null && ms.getKeyProperties().length != CommonConstants.ZERO) {
			StringBuilder keyProperties = new StringBuilder();
			for (String keyProperty : ms.getKeyProperties()) {
				keyProperties.append(keyProperty).append(",");
			}
			keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
			builder.keyProperty(keyProperties.toString());
		}
		builder.timeout(ms.getTimeout());
		builder.parameterMap(ms.getParameterMap());
		builder.resultMaps(ms.getResultMaps());
		builder.resultSetType(ms.getResultSetType());
		builder.cache(ms.getCache());
		builder.flushCacheRequired(ms.isFlushCacheRequired());
		builder.useCache(ms.isUseCache());

		return builder.build();
	}

	public static boolean isIgnoreTag(MappedStatement mappedStatement) throws ClassNotFoundException {
		String id = mappedStatement.getId();
		Class<?> classType = Class.forName(id.substring(CommonConstants.ZERO,mappedStatement.getId().lastIndexOf(StringPool.DOT)));
		//获取对应拦截方法名
		String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(StringPool.DOT) + CommonConstants.ONE);
		boolean ignore = false;
		for(Method method : classType.getDeclaredMethods()){
			if(method.isAnnotationPresent(Ignore.class) && Objects.equals(mName,method.getName())) {
				ignore = true;
			}
		}
		return ignore;
	}

	public static String getOperateType(Invocation invocation) {
		final Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[CommonConstants.ZERO];
		SqlCommandType commondType = ms.getSqlCommandType();
		if (commondType.compareTo(SqlCommandType.SELECT) == CommonConstants.ZERO) {
			return "select";
		}
		if (commondType.compareTo(SqlCommandType.INSERT) == CommonConstants.ZERO) {
			return "insert";
		}
		if (commondType.compareTo(SqlCommandType.UPDATE) == CommonConstants.ZERO) {
			return "update";
		}
		if (commondType.compareTo(SqlCommandType.DELETE) == CommonConstants.ZERO) {
			return "delete";
		}
		return null;
	}

	/**
	 * 定义一个内部辅助类，作用是包装sq
	 */
	public static class BoundSqlSqlSource implements SqlSource {
		private BoundSql boundSql;
		public BoundSqlSqlSource(BoundSql boundSql) {
			this.boundSql = boundSql;
		}
		@Override
		public BoundSql getBoundSql(Object parameterObject) {
			return boundSql;
		}
	}
}
